﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.LiveFX.ResourceModel;

namespace Wintellect.LiveFx.Cloudboard
{
  public class Configuration
  {
    private const String CloudboardConfigurationKey = "SOFTWARE\\Wintellect\\Cloudboard";
    private const String CurrentUserRunKey = "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run";

    private const String CurrentCloudboardValueName = "CurrentCloudboard";
    private const String CopyHotkeyValueName = "CopyHotkey";
    private const String InsertHotkeyValueName = "InsertHotkey";
    private const String CheckForUpdatesValueName = "CheckForUpdates";
    private const String SecretValueName = "Secret";

    private static Configuration instance;
    private Hotkey copyHotkey;
    private Hotkey insertHotkey;
    private bool checkForUpdates;
    private bool startOnLogin;
    private NotificationManager clipboardListNotificationManager;
    private System.Collections.ObjectModel.ObservableCollection<MeshObjectResource> clipboards;
    private Clipboard currentClipboard;
    private bool dirty;

    public event EventHandler ClipboardsUpdated;
    
    private Configuration()
    {
    }

    public static Configuration Instance
    {
      get
      {
        if (Configuration.instance == null)
        {
          Configuration.instance = new Configuration();
        }
        return Configuration.instance;
      }
    }

    public System.Collections.ObjectModel.ObservableCollection<MeshObjectResource> Clipboards
    {
      get
      {
        return this.clipboards;
      }
    }

    public Clipboard CurrentClipboard
    {
      get
      {
        return this.currentClipboard;
      }
      set
      {
        if (this.currentClipboard != value)
        {
          this.currentClipboard = value;
          this.dirty = true;
        }
      }
    }

    public Hotkey CopyHotkey
    {
      get
      {
        return this.copyHotkey;
      }
      set
      {
        if (this.copyHotkey != value)
        {
          this.copyHotkey = value;
          this.dirty = true;
        }
      }
    }

    public Hotkey InsertHotkey
    {
      get
      {
        return this.insertHotkey;
      }
      set
      {
        if (this.insertHotkey != value)
        {
          this.insertHotkey = value;
          this.dirty = true;
        }
      }
    }

    public bool StartOnLogin
    {
      get
      {
        return this.startOnLogin;
      }
      set
      {
        if (this.startOnLogin != value)
        {
          this.startOnLogin = value;
          this.dirty = true;
        }
      }
    }

    public bool CheckForUpdates
    {
      get
      {
        return this.checkForUpdates;
      }
      set
      {
        if (this.checkForUpdates != value)
        {
          this.checkForUpdates = value;
          this.dirty = true;
        }
      }
    }

    public System.Net.NetworkCredential GetLiveCredentials(String liveId)
    {
      System.Net.NetworkCredential output = null;
      using (Microsoft.Win32.RegistryKey cloudBoardKey = Microsoft.Win32.Registry.CurrentUser.OpenSubKey(Configuration.CloudboardConfigurationKey))
      {
        if (cloudBoardKey != null)
        {
          Byte[] encryptedCredentials = cloudBoardKey.GetValue(Configuration.SecretValueName) as Byte[];
          if (encryptedCredentials != null)
          {
            Byte[] decryptedCredentials = System.Security.Cryptography.ProtectedData.Unprotect(encryptedCredentials, null, System.Security.Cryptography.DataProtectionScope.CurrentUser);
            System.Runtime.Serialization.Formatters.Binary.BinaryFormatter formatter = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();

            Dictionary<String, String> secrets = null;
            using (System.IO.Stream stream = new System.IO.MemoryStream(decryptedCredentials))
            {
              secrets = formatter.Deserialize(stream) as Dictionary<String, String>;
            }

            if (secrets != null && secrets.ContainsKey(liveId))
            {
              output = new System.Net.NetworkCredential(liveId, secrets[liveId]);
            }
          }
        }
        return output;
      }
    }

    public void SetLiveCredentials(System.Net.NetworkCredential liveCredentials)
    {
      using (Microsoft.Win32.RegistryKey cloudBoardKey = Microsoft.Win32.Registry.CurrentUser.CreateSubKey(Configuration.CloudboardConfigurationKey))
      {
        if (cloudBoardKey != null)
        {
          System.Runtime.Serialization.Formatters.Binary.BinaryFormatter formatter = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();

          Dictionary<String, String> secrets = null;

          Byte[] existingSecrets = cloudBoardKey.GetValue(Configuration.SecretValueName, null) as Byte[];
          if (existingSecrets != null)
          {
            Byte[] decryptedData = System.Security.Cryptography.ProtectedData.Unprotect(existingSecrets, null, System.Security.Cryptography.DataProtectionScope.CurrentUser);
            System.IO.MemoryStream stream = new System.IO.MemoryStream(decryptedData);
            stream.Position = 0;
            secrets = formatter.Deserialize(stream) as Dictionary<String, String>;
          }

          if (secrets == null) secrets = new Dictionary<string, string>();

          if (liveCredentials != null)
          {
            secrets[liveCredentials.UserName] = liveCredentials.Password;

            System.IO.MemoryStream stream = new System.IO.MemoryStream();

            formatter.Serialize(stream, secrets);

            Byte[] decryptedCredentials = new Byte[stream.Length];
            stream.Position = 0;
            stream.Read(decryptedCredentials, 0, decryptedCredentials.Length);
            Byte[] encryptedCredentials = System.Security.Cryptography.ProtectedData.Protect(decryptedCredentials, null, System.Security.Cryptography.DataProtectionScope.CurrentUser);
            cloudBoardKey.SetValue(Configuration.SecretValueName, encryptedCredentials);
          }
        }
      }
    }

    public void Load()
    {
      this.DiscoverClipboards();

      using (Microsoft.Win32.RegistryKey cloudBoardKey = Microsoft.Win32.Registry.CurrentUser.CreateSubKey(Configuration.CloudboardConfigurationKey))
      {
        if (cloudBoardKey != null)
        {
          String currentCloudboardName = cloudBoardKey.GetValue(Configuration.CurrentCloudboardValueName, Constants.AppName) as String;

          var match = (from cb in this.clipboards where cb.Title == currentCloudboardName select cb).FirstOrDefault();
          if (!String.IsNullOrEmpty(currentCloudboardName) && match != null)
          {
            this.currentClipboard = new Clipboard(currentCloudboardName);
          }

          int copyHotkeyValue = Constants.CtrlAltC;
          try
          {
            copyHotkeyValue = (int)cloudBoardKey.GetValue(Configuration.CopyHotkeyValueName, Constants.CtrlAltC);
          }
          catch (InvalidCastException)
          {
          }

          int insertHotkeyValue = Constants.CtrlAltI;
          try
          {
            insertHotkeyValue = (int)cloudBoardKey.GetValue(Configuration.InsertHotkeyValueName, Constants.CtrlAltI);
          }
          catch (InvalidCastException)
          {
          }

          this.copyHotkey = Hotkey.FromInt32(copyHotkeyValue);
          this.insertHotkey = Hotkey.FromInt32(insertHotkeyValue);
          this.checkForUpdates  = Convert.ToBoolean(cloudBoardKey.GetValue(Configuration.CheckForUpdatesValueName, 1));
        }

        using (Microsoft.Win32.RegistryKey currentUserRun = Microsoft.Win32.Registry.CurrentUser.OpenSubKey(Configuration.CurrentUserRunKey))
        {
          String[] valueNames = currentUserRun.GetValueNames();
          if (valueNames.Contains(Constants.AppName))
          {
            this.startOnLogin = true;
          }
        }
      }

      this.dirty = false;
    }

    public void Save()
    {
      if (!this.dirty) return;

      using (Microsoft.Win32.RegistryKey cloudBoardKey = Microsoft.Win32.Registry.CurrentUser.CreateSubKey(Configuration.CloudboardConfigurationKey))
      {
        cloudBoardKey.SetValue(Configuration.CurrentCloudboardValueName, this.currentClipboard.Name ?? String.Empty);
        cloudBoardKey.SetValue(Configuration.CopyHotkeyValueName, this.copyHotkey.ToInt32(), Microsoft.Win32.RegistryValueKind.DWord);
        cloudBoardKey.SetValue(Configuration.InsertHotkeyValueName, this.insertHotkey.ToInt32(), Microsoft.Win32.RegistryValueKind.DWord);
        cloudBoardKey.SetValue(Configuration.CheckForUpdatesValueName, this.checkForUpdates ? 1 : 0, Microsoft.Win32.RegistryValueKind.DWord);
      }

      using (Microsoft.Win32.RegistryKey currentUserRun = Microsoft.Win32.Registry.CurrentUser.OpenSubKey(Configuration.CurrentUserRunKey, true))
      {
        String[] valueNames = currentUserRun.GetValueNames();
        if (this.startOnLogin)
        {
          currentUserRun.SetValue(Constants.AppName, System.Reflection.Assembly.GetEntryAssembly().Location, Microsoft.Win32.RegistryValueKind.String);
        }
        else if (valueNames.Contains(Constants.AppName))
        {
          currentUserRun.DeleteValue(Constants.AppName);
        }
      }

    }


    private void DiscoverClipboards()
    {
      this.clipboards = new System.Collections.ObjectModel.ObservableCollection<MeshObjectResource>();

      LiveFxClient client = new LiveFxClient();
      ServiceDocumentResource serviceDocument = client.ReadServiceDocument();
      MeshResource mesh = serviceDocument.ReadMesh(client);

      ResourceCollection<MeshObjectResource> allMeshObjects = mesh.ReadMeshObjects(client);
      this.RefreshClipboardList(allMeshObjects);

      this.clipboardListNotificationManager = new NotificationManager(client, mesh);
      this.clipboardListNotificationManager.Subscribe(allMeshObjects, (c, u) =>
      {
        this.RefreshClipboardList(mesh.ReadMeshObjects(c));
        this.OnClipboardsUpdated();
      });
      this.clipboardListNotificationManager.Start();
    }

    private void RefreshClipboardList(ResourceCollection<MeshObjectResource> meshObjects)
    {
      IEnumerable<MeshObjectResource> clipboardObjects = from mo in meshObjects.Entries
                    where (from category in mo.Categories
                           where category.Scheme == "http://wintellect.com/Cloudboard" && category.Name == "Cloudboard"
                           select category).FirstOrDefault() != null
                    select mo;

      this.clipboards.Clear();
      if (clipboardObjects != null)
      {
        foreach (MeshObjectResource resource in from clipboardResource in clipboardObjects select clipboardResource)
        {
          this.clipboards.Add(resource);
        }
      }
    }

    private void OnClipboardsUpdated()
    {
      if (this.ClipboardsUpdated != null)
      {
        this.ClipboardsUpdated(this, new EventArgs());
      }
    }
  }
}
